package cn.lysoy.demo.aspect;

import cn.lysoy.demo.entity.RequestLog;
import cn.lysoy.demo.service.RequestLogService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Component
@Aspect
@Slf4j
public class ControllerAspect {
    @Resource
    @Qualifier("requestLogService")
    private RequestLogService requestLogService;

//    @Autowired
//    private MongoTemplate mongoTemplate;

    private String ip = "";
    private String className; // request类名
    private String methodName;      // 方法名
    private String requestUrl; // 请求地址
    private String requestHeader;
    private String requestBody; // 传入参数
    private Map<String, Object> returnValue = null; // 存放输出结果
    private long startTimeMillis = 0; // 开始时间
    private long endTimeMillis = 0; // 结束时间
    private HttpServletRequest request = null;
    private Date requestDate = null;

    @Pointcut("execution( public * cn.lysoy.demo.controller.*.*(..))")
    public void aopPointCut() {
    }

    /**
     * @Description: 获取request
     */
    public HttpServletRequest getHttpServletRequest(){
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        HttpServletRequest request = sra.getRequest();
        return request;
    }
    /**
     * @Description: 获取请求参数
     **/
    public String getRequestParam(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        // 参数名数组
        String[] parameterNames = ((MethodSignature) signature).getParameterNames();
        if (null == parameterNames || parameterNames.length == 0) {
            return "";
        }
        // 构造参数组集合
        HashMap<Object,Object> inputParamMap = new HashMap<>();
        Object[] args = joinPoint.getArgs();
        for (int i = 0; i < args.length; i++) {
            //request/response无法使用toJSON
            if (args[i] instanceof HttpServletRequest) {
                inputParamMap.put(JSON.toJSON(parameterNames[i]), "request");
            } else if (args[i] instanceof HttpServletResponse) {
                inputParamMap.put(JSON.toJSON(parameterNames[i]), "response");
            } else {
                try {
                    inputParamMap.put(JSON.toJSON(parameterNames[i]), JSON.toJSON(args[i]));
                }catch (Exception e) {

                }

            }
        }
        return JSON.toJSON(inputParamMap).toString();
    }

    /**
     * 获取请求头
     * @param
     * @return null
     * @author SongYang
     * @date 2022/06/27 09:51
     */
    public String getRequestHeader(HttpServletRequest request) {
        //1.获取所有请求头名称
        Enumeration<String> headerNames = request.getHeaderNames();
        //2.遍历
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        while(headerNames.hasMoreElements()){
            String name = headerNames.nextElement();
            sb.append(name);
            sb.append(":");
            //根据名称获取请求头的值
            String header = request.getHeader(name);
            sb.append(header);
            sb.append(",");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append("}");
        return sb.toString();
    }

    @Before("aopPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        this.className = joinPoint.getTarget().getClass().getName();
        this.methodName = joinPoint.getSignature().getName();
        this.request = getHttpServletRequest();
        this.ip = request.getRemoteAddr();
        this.requestUrl = this.request.getRequestURL().toString();
        this.requestBody = getRequestParam(joinPoint);
        this.startTimeMillis = System.currentTimeMillis();
        this.requestHeader = getRequestHeader(this.request);
        this.requestDate = new Date();
    }

    /**
     * @Description: 方法调用后触发   记录
     */
    @AfterReturning(value = "aopPointCut()", returning = "returnValue")
    public void after(JoinPoint joinPoint, Object returnValue) {
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = null;
        try {
            targetClass = Class.forName(this.className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Method[] methods = targetClass.getMethods();
        for (Method method : methods) {
            if (method.getName().equals(this.methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs != null && clazzs.length == arguments.length && method.getAnnotation(RequestMapping.class) != null) {
                    String message = returnValue.toString();
                    if (message.length() > 1000) {
                        message = message.substring(0, 1000);
                    }
                    this.endTimeMillis = System.currentTimeMillis();
                    //获取IP地址
                    RequestMethod[] requestMethods = method.getAnnotation(RequestMapping.class).method();
                    int length = requestMethods.length;
                    StringBuilder sb = new StringBuilder(length*2+1);
                    sb.append("{");
                    for (int i = 0; i < length - 1; i++) {
                        sb.append(requestMethods[i]);
                        sb.append(",");
                    }
                    sb.append(requestMethods[length-1]);
                    sb.append("}");
                    String requestMethod = sb.toString();
                    RequestLog requestLog = new RequestLog();
                    requestLog.setClassName(this.className);
                    requestLog.setMethodName(this.methodName);
                    requestLog.setRequestIp(this.ip);
                    requestLog.setReturnValue(message);
                    requestLog.setRequestUrl(this.requestUrl);
                    requestLog.setRequestMethod(requestMethod);
                    requestLog.setRequestBody(this.requestBody);
                    requestLog.setRequestDate(this.requestDate);
                    requestLog.setRequestHeader(this.requestHeader);
                    requestLog.setHandleTime(this.endTimeMillis-this.startTimeMillis);
                    try {
                        requestLogService.save(requestLog);
                    }catch (Exception e) {
                        log.info(e.getMessage());
                    }


//                    try {
//                        SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd");
//                        Calendar calendar = Calendar.getInstance();
//
//                        Date dateObj = calendar.getTime();
//                        String formattedDate = dtf.format(dateObj);
//                        if (!mongoTemplate.collectionExists(formattedDate)) {
//                            mongoTemplate.createCollection(formattedDate);
//                        }
//                        mongoTemplate.save(requestLog, formattedDate);
//                    }catch (Exception e) {
//                        log.info(e.getMessage());
//                    }

                }}}}


    @Around("aopPointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long end = System.currentTimeMillis();
            log.info("======Controller around " + joinPoint + "\t 耗时为：: " + (end - start) + " ms!");
            return result;
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            log.info("======Controller around " + joinPoint + "\t 耗时为：: " + (end - start) + " ms with exception : " + e.getMessage());
            throw e;
        }
    }
}

